#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <apra/inet.h>
#include <netinet/in.h>

#include "tknetbusi.h"
#include "tklist.h"
#include "tknetutils.h"
#include "tkconstants.h"
#include "tkerror.h"
#include "tkconstants.h"
#include "tklog.h"

struct tk_connect* get_tk_connect(void *s)
{
	struct tk_connect *conn = (struct tk_connect*)malloc(sizeof(struct tk_connect));
	if (!conn)	
		return NULL;

	INIT_LIST_HEAD(&conn->list_node);

	memset(conn, 0x00, sizeof(struct tk_connect));
	conn->csock = s;
	if (!conn->csock) { 
		free(conn);
		return NULL;
	}

	conn->csock->ptr = conn;	
	
	return conn;
}

void destory_tk_connect(struct tk_connect *conn)
{
	if (conn == NULL)
		return;

	tk_socket_error(conn->csock);

	conn->csock->ptr = NULL;
	conn->csock = NULL;

	free(conn);		
}

int init_tk_message(struct tk_message *msg, char *body, int body_len, int msg_type)
{
	if (body_len > MAXMESSAGELEN)
		return TK_TOO_LONG_MESSAGE; 

	msg->header_len = HEADERLEN;
	msg->header[HEADERLEN] = '\0';
	sprintf(msg->header, "%4d%4d", body_len, msg_type);
	msg->body_len = body_len;
	memcpy(msg->body, body, body_len);
	msg->body[body_len] = '\0';

	return TK_OK;
}


void tk_recv_head_init(struct tk_connect *conn)
{
	/* need to recv head len, not represent aready having buf len */

	memset(&conn->recv_msg, 0x00, sizeof(struct tk_message));
	conn->recv_msg.header_len = HEADERLEN;
	conn->recving_head = 1;
	conn->recving_body = 0;
}

void tk_send_head_init(struct tk_connect *conn)
{
	conn->sending_head = 1;
	conn->sending_body = 0;
}

/* @RETURN:        1. TK_OK 2. TK_AGAIN 3. TK_ERROR 4. TK_DONE	(remote close)	*/
int tk_recv_head(struct tk_connect *conn)
{
	int r = 0;
	struct custom_socket *s = conn->csock;
	int tmp_recv_len = conn->recv_msg.header_len;
		
	r = tk_recvn(s->sockfd, conn->recv_msg.header + conn->recv_msg.header_len, &tmp_recv_len);
	conn->recv_msg.header_len -= tmp_recv_len;

	return r;
}

/* @RETURN:        1. TK_AGAIN 2. TK_ERROR(errno) *3. TK_DONE(no use)* 4. TK_OK	*/
int tk_send_head(struct tk_connect *conn)
{
	int r = 0;
	struct custom_socket *s = conn->csock;
	struct tk_message *msg = &conn->send_msg[conn->msg_start];
	int tmp_send_len = msg->header_len;

	r = tk_sendn(s->sockfd, msg->header, &tmp_send_len);
	if (r != TK_ERROR) {
		msg->header_len -= tmp_send_len;
		memmove(msg->header, msg->header + tmp_send_len, msg->header_len);
	}

	return r;
}

void tk_recv_body_init(struct tk_connect *conn)
{
	/* other infomation initialized by head init */

	conn->recving_head = 0;
	conn->recving_body = 1;
}

void tk_send_body_init(struct tk_connect *conn)
{

	conn->sending_head = 0;
	conn->sending_body = 1;
}

int tk_recv_body(struct tk_connect *conn) 
{
	int r = 0;
	int need_recv_len = 0;
	struct custom_socket *s = conn->csock;
	struct tk_message *msg = &conn->recv_msg;

	need_recv_len = msg->body_len;

        r = tk_recvn(s->sockfd, msg->body, &need_recv_len);
	msg->body_tmp_len -= need_recv_len;

        return r;		
}

int tk_send_body(struct tk_connect *conn)
{
	int r = 0;
	int need_send_len = 0;
	struct custom_socket *s = conn->csock;
	struct tk_message *msg = &conn->send_msg[conn->msg_start];

	need_send_len = msg->body_len;

	r = tk_sendn(s->sockfd, msg->body, &need_send_len);
	if (r != TK_ERROR) {
		msg->body_tmp_len -= need_send_len;
		memmove(&msg->body, &msg->body + need_send_len, msg->body_tmp_len); 
	}

	return r;
}

/***
@RETURN:        
	1. TK_AGAIN (internal buffer is full)
	2. TK_ERROR(errno) 
	3. TK_DONE(no use) 
	4. TK_OK (head sent) should delete event(if non-blocking mode) no use 
	5. TK_NO_MORE_SEND_MSG (attention: other success status) should delete event(if non-blocking mode) 
	6. TK_SEND_BODY_DONE: should delete event(if non-blocking mode)
at most send one message
***/
int tk_do_send_msg(struct tk_connect *conn)
{
	int r = 0;
	struct custom_socket *s = conn->csock;
	
	if (conn->sending_head == 0 && conn->sending_body == 1) 
		tk_send_head_init(conn);

	if (conn->sending_head == 1) {
		r = tk_send_head(conn);
		if (r == TK_OK) {
			tk_send_body_init(conn);		
		}
		else {
			return r ;
		}

		/* return r;  direct recveive body not return */
	}
	/* caution: not else if contidion */
	if (conn->sending_body == 1) {
		r = tk_send_body(conn);
		if (r == TK_OK) {
			/* next message */
			conn->msg_start = (conn->msg_start + 1) % MAXMESSAGENUM;

			r = TK_SEND_BODY_DONE;

			/* no more message to send */
			if (conn->msg_start == conn->msg_end) {
				conn->sending_head = 0;
				conn->sending_body = 0;

				return TK_NO_MORE_SEND_MSG; 
			}
			else {
				tk_send_head_init(conn);
			}
		}
	
		return r;
	}

	/* not possible */
	return TK_ERROR;
}

struct tk_message* get_send_message(struct tk_connect *conn)
{
	if (conn->msg_start == conn->msg_end)
		return NULL;

	return &conn->send_msg[conn->msg_start]; 
}

/* 
@RETURN:        
	1. TK_OK (head received, not use)
	2. TK_AGAIN (internal buffer is empty)
	3. TK_ERROR 
	4. TK_DONE	(remote close)	
	5. TK_RECV_BODY_DONE should delete event(if non-blocking mode)
at most receive one message
*/
int tk_do_recv_msg(struct tk_connect *conn) 
{
	int r = 0;
	struct custom_socket *s = conn->csock;

	if (conn->recving_head == 0 && conn->recving_body == 0) {
		tk_recv_head_init(conn);
	}

	if (conn->recving_head == 1) {
		r = tk_recv_head(conn);
		if (r == TK_OK) {
			conn->recv_msg.header[HEADERLEN] = '\0';
			conn->recv_msg.body_tmp_len = atoi(conn->recv_msg.header);
			conn->recv_msg.body_tmp_len = conn->recv_msg.body_len;
			elog("receive message [%s] - [%d]", conn->recv_msg.header, conn->recv_msg.body_len);
			tk_recv_body_init(conn);
		}
		else {
			return r;
		}
		/* return r;  direct recveive body not return */
	}
	/* caution: not else if contidion */
	if (conn->recving_body == 1) {
		r = tk_recv_body(conn);
		if (r == TK_OK) {
			conn->recving_head = 0;
			conn->recving_body = 0;

			/* whole message received */
			r = TK_RECV_BODY_DONE;
		}

		tk_recv_head_init(conn);

		return r;	
	}

	/* not possible */
	return TK_ERROR;
}

struct tk_message* get_recv_message(struct tk_connect *conn)
{
	return &conn->recv_msg;
}

/**
	memcpy send message to buffer
@RETURN
	1. TK_NEED_ADD_W_EVENT
	2. TK_OK
	add message and event will auto send messages in buffer
**/ 
int tk_do_add_send_msg(struct tk_connect *conn, struct tk_message *send_msg)
{
	int need_add_write_event = 0;
	struct custom_socket *s = conn->csock;
        struct tk_message *msg = NULL;

	if ((conn->msg_start + 1) % MAXMESSAGENUM == conn->msg_end) {
		need_add_write_event = 1;
	}

        if ((conn->msg_end + 1) % MAXMESSAGENUM == conn->msg_start) {
                return TK_TOO_MUCH_SEND_MSG;
        }

        msg = &conn->send_msg[conn->msg_end];
        conn->msg_end = (conn->msg_end + 1) % MAXMESSAGENUM;

        /* copy msg info */
        memcpy(msg, send_msg, sizeof(struct tk_message));
        /* need to send head len */
        msg->header_len = HEADERLEN;

	if (need_add_write_event) {
		return TK_NEED_ADD_W_EVENT;
	}
	else {
		return TK_OK;
	}
}

void tk_start_communicate(struct tk_connect *conn)
{
	conn->stage = TK_CONNECT_COMMUNICATING;
}

int tk_connection_error(struct select_env *env, struct tk_connect *conn)
{
	struct custom_socket *s = conn->csock;

	if (!list_empty(&conn->list_node)) {
		list_del_init(&conn->list_node);
		if (main_delete_member((struct member*)conn->client_ptr)) {
			elog("member not in member list");
		}
	}

        elog("ip[%s]-port[%d] - stage[%d] error[%d]", s->addr.ipv4, s->addr.port, conn->stage, errno);
        destory_tk_connect(conn);

        return TK_OK;
}

int tk_remote_close(struct select_env *env, struct tk_connect *conn)
{
	struct custom_socket *s = conn->csock;

	if (!list_empty(&conn->list_node)) {
		list_del_init(&conn->list_node);
		if (main_delete_member((struct member*)conn->client_ptr)) {
			elog("member not in member list");
		}
	}

        elog("ip[%s]-port[%d] - stage[%d]", s->addr.ipv4, s->addr.port, conn->stage);
        destory_tk_connect(conn);

        return TK_OK;
}

struct tk_connect* tk_connect_server(struct select_env *env, char *ip, int serverport)
{
	struct tk_connect *conn = NULL;
	struct tk_socket* s = NULL;
	struct sockaddr_in sockaddr;
	
	if ((s = get_custom_socket()) == NULL) {
		return NULL;
	}

	conn = get_tk_connect(s);
	if (conn == NULL) {
		return NULL;
	}

	s->sockfd = socket(AF_NET, SOCK_STREAM, 0);
	if (s->sockfd == -1) {
		elog("socket failed, errno[%d]", errno);
		destory_tk_connect(conn);
		return NULL;	
	}

        memset(&sockaddr, 0x00, sizeof(struct sockaddr_in));
        sockaddr.sin_port = htons(serverport);
        sockaddr.sin_family = PF_INET;
        if (inet_pton(sockaddr.sin_family, ip, &sockaddr.sin_addr.s_addr) != 1) {
		elog("inet_pton failed, ip[%s]", ip);
		destory_tk_connect(conn);
		return NULL;
	}

	r = set_nonblocking(s->sockfd);
	if (r) {
		elog("set sock nonblock failed");
		destory_tk_connect(conn);
		return NULL;
	}

	r = set_tcpkeepalive(s->sockfd)
	if (r) {
		elog("set tcp alive failed");
		destory_tk_connect(conn);
		return NULL;
	}

	r = tk_connect_non_block(s->sockfd, &sockaddr, sizeof(struct sockaddr_in));
	if (r == TK_AGAIN || r == TK_OK) {
		
		/* add read and write event , add first handler */
		r = select_add_rw_event(struct select_env *env, void *event)
		if (r) {
			elog("select add rw event failed");
			destory_tk_connect(conn);
			return NULL;	
		}
		s->handler  = hd_test_connect;

		return conn;
	}
	else if (r) {
		elog("connect failed, ip=[%s] port[%d]", ip, serverport);
		destory_tk_connect(conn);
		return NULL;
	}
}

int tk_message_body_len(struct tk_message *msg)
{
	return msg->body_len;
}

int tk_message_type(struct tk_message *msg)
{
	return atoi(&msg->header[4]);
}
